﻿using System;
using System.Collections.Concurrent;
using System.Threading;
using Godot;

namespace kemocard.Scripts.Common;

/// <summary>
/// 通用对象池实现
/// </summary>
/// <typeparam name="T">池中对象的类型</typeparam>
public class ObjectPool<T> : IDisposable where T : class
{
    private readonly ConcurrentBag<T> _objects;
    private readonly Func<T> _objectGenerator;
    private readonly Action<T> _objectResetter;
    private readonly int _maxSize;
    private int _currentCount;

    /// <summary>
    /// 初始化对象池
    /// </summary>
    /// <param name="objectGenerator">对象创建工厂方法</param>
    /// <param name="objectResetter">对象重置方法（可选）</param>
    /// <param name="initialCapacity">初始容量</param>
    /// <param name="maxSize">最大容量（-1表示无限制）</param>
    public ObjectPool(
        Func<T> objectGenerator,
        Action<T> objectResetter = null,
        int initialCapacity = 10,
        int maxSize = -1)
    {
        _objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
        _objectResetter = objectResetter;
        _maxSize = maxSize;
        _objects = [];
        _currentCount = 0;

        // 预创建初始容量的对象
        for (var i = 0; i < initialCapacity; i++)
        {
            var obj = _objectGenerator();
            if (obj == null) continue;
            _objects.Add(obj);
            Interlocked.Increment(ref _currentCount);
        }
    }

    /// <summary>
    /// 从对象池中获取一个对象
    /// </summary>
    /// <returns>对象实例</returns>
    public T Get()
    {
        if (!_objects.TryTake(out T item)) return _objectGenerator();
        Interlocked.Decrement(ref _currentCount);
        return item;

        // 如果没有可用对象，则创建新对象
    }

    /// <summary>
    /// 将对象归还到对象池
    /// </summary>
    /// <param name="item">要归还的对象</param>
    public void Return(T item)
    {
        if (item == null) return;

        // 重置对象状态
        _objectResetter?.Invoke(item);

        // 如果设置了最大容量且当前数量已达到上限，则不归还对象
        if (_maxSize > 0 && _currentCount >= _maxSize)
        {
            // 超出容量限制的对象直接丢弃，让GC处理
            if (item is IDisposable disposable)
            {
                disposable.Dispose();
            }

            if (item is Node node)
            {
                node.QueueFree();
            }

            return;
        }

        _objects.Add(item);
        Interlocked.Increment(ref _currentCount);
    }

    /// <summary>
    /// 获取当前池中可用对象数量
    /// </summary>
    public int Count => _currentCount;

    /// <summary>
    /// 清空对象池
    /// </summary>
    public void Clear()
    {
        while (_objects.TryTake(out T item))
        {
            if (item is IDisposable disposable)
            {
                disposable.Dispose();
            }

            if (item is Node node)
            {
                node.QueueFree();
            }
        }

        _currentCount = 0;
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        Clear();
    }
}